#pragma once

#include <iostream>
// struct tm{};
#include <ctime>
// va_list
#include <cstdarg>
// open();
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>
// __FILE__
#include <stdio.h>

// 信息类别

#define INFO 1
#define WARNING 2
#define ERROR 3
#define FATAL 4

// 日志文件名称
#define LogFile "log.txt"

// 输出目的地
#define SCREEN 1
#define LOGFILE 2   //./log.txt
#define Classfile 3 //./log.txt.debug

// 缓冲区大小
#define SIZE 1024
class MyLog
{
private:
    int _printMethod;       // 输出方法
    std::string _printPath; // 输出文件路径

public:
    MyLog()
    {
        _printMethod = SCREEN;
        _printPath = "./";
    }

    // log(Warning, "accept error, %s: %d", strerror(errno), errno);
    void operator()(std::string level, const char *fileName, int fileLine, const char *msg_format, ...)
    {
        // 构建日志内容 打印到指定处

        // 1. level+年月日
        time_t timestamp = time(nullptr);
        struct tm *ctime = localtime(&timestamp);
        char partOne[SIZE];
        snprintf(partOne, sizeof(partOne), "[%s][%d-%d-%d %d:%d:%d]", level.c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
        // %s %d %s %s __FILE__, __LINE__,  __DATE__,__TIME__
        // log.hpp 103 Aug 16 2024 18:14:54

        // 2. 自定义msg
        va_list arg_list;               // 存储可变参数列表信息
        va_start(arg_list, msg_format); // 初始化 使其指向函数参数列表中format参数之后的第一个可变参数
        char partTwo[SIZE];
        vsnprintf(partTwo, sizeof(partTwo), msg_format, arg_list); // s: safe v: 输出到字符串缓冲区
        va_end(arg_list);                                          // 清理va_list变量

        // 3. 行号 + 文件名
        char partThree[SIZE];
        snprintf(partThree, sizeof(partThree), "[%s:%d]", fileName, fileLine);

        // 合并日志信息
        char log_content[SIZE * 3];
        snprintf(log_content, sizeof(log_content), "%s %s %s", partOne, partTwo, partThree);

        // 调用printLog printLog根据当前printMethod 输出到指定处
        printLog(_printMethod, level, log_content);
    }

    void printLog(int printMethod, std::string level, const std::string &log_content)
    {
        switch (printMethod)
        {
        case SCREEN:
            // std::cout << log_content << std::endl;
            std::cerr << log_content << std::endl;
            break;
        case LOGFILE:
            printOneFile(LogFile, log_content);
            break;
        case Classfile:
            printClassFile(level, log_content);
            break;
        default:
            break;
        }
    }

    // 输出到普通文件 ./"log.txt"
    void printOneFile(const std::string &log_filename, const std::string &log_content)
    {
        // path = "./"; #define LogFile "log.txt"
        std::string _logFilename = _printPath + log_filename;
        // 0666：所有用户都可读写但不可执行
        int fd = open(_logFilename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"
        if (fd < 0)
            return;
        write(fd, log_content.c_str(), log_content.size());
        close(fd);
    }

    // 输出到类别文件 "log.txt.Debug"
    void printClassFile(std::string level, const std::string &log_content)
    {
        // #define LogFile "log.txt"
        std::string filename = LogFile;
        filename += ".";
        filename += level; // "log.txt.Debug"
        printOneFile(filename, log_content);
    }
};
MyLog mylog;
#define LOG(level, format, ...) mylog(#level, __FILE__, __LINE__, format, ##__VA_ARGS__)

